Let’s continue the investigation of LINQ to XML with a new Console Application named ConstructingXmlDocs. Once you have done so, import the System.Xml.Linq namespace in your initial code file. As you have already seen, XDocument represents the entirety of an XML document in the LINQ to XML programming model, as it can be used to define a root element, and all contained elements, processing instructions and XML declarations. Here is another example of building XML data using XDocument:
static void CreateFullXDocument() { XDocument inventoryDoc = new XDocument( new XDeclaration("1.0", "utf-8", "yes"), new XComment("Current Inventory of cars!"), new XProcessingInstruction("xml-stylesheet", "href='MyStyles.css' title='Compact' type='text/css'"), new XElement("Inventory", new XElement("Car", new XAttribute("ID", "1"), new XElement("Color", "Green"), new XElement("Make", "BMW"), new XElement("PetName", "Stan") ), new XElement("Car", new XAttribute("ID", "2"), new XElement("Color", "Pink"), new XElement("Make", "Yugo"), new XElement("PetName", "Melvin") ) ) ); // Save to disk. inventoryDoc.Save("SimpleInventory.xml"); }
Again, notice that the constructor of the XDocument object is in fact a tree of additional LINQ to XML objects. The constructor called here takes as the first parameter an XDeclaration, followed by a parameter array of objects (recall, C# parameter arrays allow you to pass in a comma delimited list of arguments, which are packaged as an array on your behalf):
public XDocument(System.Xml.Linq.XDeclaration declaration, params object[] content)
If you were to invoke this method from Main(), you’d see the following data in the SimpleInventory.xml file:
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <!--Current Inventory of cars!--> <?xml-stylesheet href='MyStyles.css' title='Compact' type='text/css'?> <Inventory> <Car ID="1"> <Color>Green</Color> <Make>BMW</Make> <PetName>Stan</PetName> </Car> <Car ID="2"> <Color>Pink</Color> <Make>Yugo</Make> <PetName>Melvin</PetName> </Car> </Inventory>
As it turns out, the default XML declaration for any XDocument is to use utf-8 encoding, XML version 1.0, as a standalone document. Therefore, you could completely delete the creation of the XDeclaration object and end up with the same data; given that just about every document requires this same declaration, use of XDeclaration is typically not that common.
If you do not need to define processing instructions or a custom XML declaration, you can avoid the use of XDocument all together, and simply use XElement. Remember, XElement can be used to represent the root element of the XML document and all sub-objects. Thus, you could generate a commented list of inventory items as so:
static void CreateRootAndChildren() { XElement inventoryDoc = new XElement("Inventory", new XComment("Current Inventory of cars!"), new XElement("Car", new XAttribute("ID", "1"), new XElement("Color", "Green"), new XElement("Make", "BMW"), new XElement("PetName", "Stan") ), new XElement("Car", new XAttribute("ID", "2"), new XElement("Color", "Pink"), new XElement("Make", "Yugo"), new XElement("PetName", "Melvin") ) ); // Save to disk. inventoryDoc.Save("SimpleInventory.xml"); }
The output is more or less identical, sans the custom processing instruction for a hypothetical style sheet:
<?xml version="1.0" encoding="utf-8"?> <Inventory> <!--Current Inventory of cars!--> <Car ID="1"> <Color>Green</Color> <Make>BMW</Make> <PetName>Stan</PetName> </Car> <Car ID="2"> <Color>Pink</Color> <Make>Yugo</Make> <PetName>Melvin</PetName> </Car> </Inventory>
So far you have been building XML documents using fixed hard coded constructor values. More commonly, you will need to generate XElements (or XDocuments) by reading data from arrays, ADO.NET objects, file data, or whatnot . One way to map in-memory data to a new XElement is by using a set of standard “for loops” to move data into the LINQ to XML object model. While this is certainly doable, it is more streamlined to embed a LINQ query within the construction of the XElement directly.
Assume you have an anonymous array of anonymous classes (just to avoid the amount of code for this example; any array, List<T> or other container would do here). You could map this data into an XElement as so:
static void MakeXElementFromArray() { // Create an anonymous array of anonymous types. var people = new[] { new { FirstName = "Mandy", Age = 32}, new { FirstName = "Andrew", Age = 40 }, new { FirstName = "Dave", Age = 41 }, new { FirstName = "Sara", Age = 31} }; XElement peopleDoc = new XElement("People", from c in people select new XElement("Person", new XAttribute("Age", c.Age), new XElement("FirstName", c.FirstName)) ); Console.WriteLine(peopleDoc); }
Here, the peopleDoc object defines the root <People> element with the results of a LINQ query. This LINQ query creates new XElements based on each item in the people array. If this embedded query is a bit hard on the eyes, you could break things down into explicit steps, like so:
static void MakeXElementFromArray() { // Create an anonymous array of anonymous types. var people = new[] { new { FirstName = "Mandy", Age = 32}, new { FirstName = "Andrew", Age = 40 }, new { FirstName = "Dave", Age = 41 }, new { FirstName = "Sara", Age = 31} }; var arrayDataAsXElements = from c in people select new XElement("Person", new XAttribute("Age", c.Age), new XElement("FirstName", c.FirstName)); XElement peopleDoc = new XElement("People", arrayDataAsXElements); Console.WriteLine(peopleDoc); }
Either way, the output is the same:
<People> <Person Age="32"> <FirstName>Mandy</FirstName> </Person> <Person Age="40"> <FirstName>Andrew</FirstName> </Person> <Person Age="41"> <FirstName>Dave</FirstName> </Person> <Person Age="31"> <FirstName>Sara</FirstName> </Person> </People>
The XElement and XDocument types both support Load() and Parse() methods, which allow you to hydrate an XML object model from string objects containing XML data or external XML files. Consider the following method, which illustrates both approaches:
static void ParseAndLoadExistingXml() { // Build an XElement from string. string myElement = @"<Car ID ='3'> <Color>Yellow</Color> <Make>Yugo</Make> </Car>"; XElement newElement = XElement.Parse(myElement); Console.WriteLine(newElement); Console.WriteLine(); // Load the SimpleInventory.xml file. XDocument myDoc = XDocument.Load("SimpleInventory.xml"); Console.WriteLine(myDoc); }
Source Code The ConstructingXmlDocs example can be found under the Chapter 24 subdirectory.